<!DOCTYPE html>
<html>
<head>
    <title>VDO.Ninja DataChannel Pub/Sub Example</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
        }
        .container {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 20px;
        }
        .section {
            border: 1px solid #ddd;
            padding: 20px;
            border-radius: 8px;
        }
        .messages {
            height: 300px;
            overflow-y: auto;
            border: 1px solid #eee;
            padding: 10px;
            margin: 10px 0;
            background: #f5f5f5;
        }
        .message {
            margin: 5px 0;
            padding: 5px;
            background: white;
            border-radius: 4px;
        }
        .controls {
            display: flex;
            gap: 10px;
            margin: 10px 0;
        }
        input, button, select {
            padding: 8px;
            font-size: 14px;
        }
        input[type="text"] {
            flex: 1;
        }
        .status {
            padding: 10px;
            background: #e0e0e0;
            border-radius: 4px;
            margin: 10px 0;
        }
        .status.connected {
            background: #c8e6c9;
        }
        .status.error {
            background: #ffcdd2;
        }
        .label-badge {
            display: inline-block;
            padding: 2px 8px;
            background: #2196F3;
            color: white;
            border-radius: 12px;
            font-size: 12px;
            margin: 0 4px;
        }
        h3 {
            margin-top: 0;
        }
        code {
            background: #f0f0f0;
            padding: 2px 4px;
            border-radius: 3px;
            font-size: 13px;
        }
    </style>
</head>
<body>
    <h1>VDO.Ninja DataChannel Pub/Sub Example</h1>
    <p>This example demonstrates replacing IFRAME-based messaging with the DataChannel SDK.</p>
    
    <div class="container">
        <div class="section">
            <h3>Publisher Node</h3>
            <div class="status" id="pubStatus">Disconnected</div>
            
            <div class="controls">
                <input type="text" id="pubRoom" placeholder="Room name" value="demo-room">
                <input type="text" id="pubLabel" placeholder="Label" value="sensor-1">
                <button onclick="connectPublisher()">Connect</button>
            </div>
            
            <h4>Publish Message</h4>
            <div class="controls">
                <input type="text" id="pubMessage" placeholder="Message to send">
                <select id="pubTarget">
                    <option value="all">Broadcast to All</option>
                    <option value="label">To Label</option>
                    <option value="streamid">To Stream ID</option>
                </select>
                <input type="text" id="pubTargetValue" placeholder="Target label/ID" style="display:none;">
                <button onclick="publishMessage()">Send</button>
            </div>
            
            <h4>Stream Data</h4>
            <div class="controls">
                <input type="file" id="fileInput">
                <button onclick="streamFile()">Stream File</button>
            </div>
            
            <div class="messages" id="pubMessages"></div>
        </div>
        
        <div class="section">
            <h3>Subscriber Node</h3>
            <div class="status" id="subStatus">Disconnected</div>
            
            <div class="controls">
                <input type="text" id="subRoom" placeholder="Room name" value="demo-room">
                <input type="text" id="subLabel" placeholder="Label" value="controller">
                <button onclick="connectSubscriber()">Connect</button>
            </div>
            
            <h4>Subscribe to Labels</h4>
            <div class="controls">
                <input type="text" id="subToLabel" placeholder="Label to subscribe" value="sensor-1">
                <button onclick="subscribeToLabel()">Subscribe</button>
            </div>
            
            <div id="subscriptions"></div>
            
            <h4>Received Messages</h4>
            <div class="messages" id="subMessages"></div>
            
            <h4>Received Streams</h4>
            <div id="streams"></div>
        </div>
    </div>
    
    <div class="section" style="margin-top: 20px;">
        <h3>Mesh Network Status</h3>
        <div id="meshStatus"></div>
        <canvas id="meshCanvas" width="800" height="400" style="border: 1px solid #ddd;"></canvas>
    </div>
    
    <div class="section" style="margin-top: 20px;">
        <h3>Code Example (Replacing IFRAME)</h3>
        <h4>Before (with IFRAME):</h4>
        <pre><code>&lt;iframe src="https://vdo.ninja/?room=myroom&amp;push=cam1&amp;datachannel=true" 
        style="display:none"&gt;&lt;/iframe&gt;

// Communicate via postMessage
iframe.contentWindow.postMessage({data: 'hello'}, '*');</code></pre>
        
        <h4>After (with SDK):</h4>
        <pre><code>const node = new VDONinjaDataChannel();
await node.joinRoom({room: 'myroom', streamID: 'cam1'});

// Direct data channel communication
node.publish({data: 'hello'});

// Subscribe to specific labels
node.subscribe('sensors');
node.addEventListener('data', (e) => {
    console.log('Received:', e.detail.data);
});</code></pre>
    </div>

    <script src="../vdoninja-datachannel-sdk.js"></script>
    <script>
        let publisher = null;
        let subscriber = null;
        const meshNodes = new Map();
        
        // Target selection handler
        document.getElementById('pubTarget').addEventListener('change', (e) => {
            const targetInput = document.getElementById('pubTargetValue');
            if (e.target.value === 'all') {
                targetInput.style.display = 'none';
            } else {
                targetInput.style.display = 'block';
                targetInput.placeholder = e.target.value === 'label' ? 'Target label' : 'Target stream ID';
            }
        });
        
        async function connectPublisher() {
            if (publisher) {
                publisher.disconnect();
            }
            
            publisher = new VDONinjaDataChannel({
                debug: true,
                meshMode: 'partial'
            });
            
            // Setup event handlers
            publisher.addEventListener('connected', () => {
                updateStatus('pubStatus', 'Connected', true);
                addMessage('pubMessages', 'Connected to server', 'system');
            });
            
            publisher.addEventListener('disconnected', () => {
                updateStatus('pubStatus', 'Disconnected', false);
            });
            
            publisher.addEventListener('peer-connected', (e) => {
                addMessage('pubMessages', `Peer connected: ${e.detail.uuid}`, 'system');
                updateMeshVisualization();
            });
            
            publisher.addEventListener('data', (e) => {
                addMessage('pubMessages', 
                    `Received from ${e.detail.label || e.detail.streamID}: ${JSON.stringify(e.detail.data)}`, 
                    'received'
                );
            });
            
            try {
                await publisher.connect();
                await publisher.joinRoom({
                    room: document.getElementById('pubRoom').value,
                    streamID: `pub-${Date.now()}`,
                    label: document.getElementById('pubLabel').value
                });
                
                meshNodes.set(publisher.uuid, {
                    sdk: publisher,
                    type: 'publisher',
                    label: publisher.label
                });
                
                updateStatus('pubStatus', `Connected as ${publisher.label}`, true);
            } catch (error) {
                updateStatus('pubStatus', `Error: ${error.message}`, false);
            }
        }
        
        async function connectSubscriber() {
            if (subscriber) {
                subscriber.disconnect();
            }
            
            subscriber = new VDONinjaDataChannel({
                debug: true,
                meshMode: 'partial'
            });
            
            // Setup event handlers
            subscriber.addEventListener('connected', () => {
                updateStatus('subStatus', 'Connected', true);
                addMessage('subMessages', 'Connected to server', 'system');
            });
            
            subscriber.addEventListener('disconnected', () => {
                updateStatus('subStatus', 'Disconnected', false);
            });
            
            subscriber.addEventListener('peer-connected', (e) => {
                addMessage('subMessages', `Peer connected: ${e.detail.uuid}`, 'system');
                updateMeshVisualization();
            });
            
            subscriber.addEventListener('data', (e) => {
                addMessage('subMessages', 
                    `From ${e.detail.label || e.detail.streamID}: ${JSON.stringify(e.detail.data)}`, 
                    'received'
                );
            });
            
            subscriber.addEventListener('stream', (e) => {
                handleReceivedStream(e.detail);
            });
            
            try {
                await subscriber.connect();
                await subscriber.joinRoom({
                    room: document.getElementById('subRoom').value,
                    streamID: `sub-${Date.now()}`,
                    label: document.getElementById('subLabel').value
                });
                
                meshNodes.set(subscriber.uuid, {
                    sdk: subscriber,
                    type: 'subscriber',
                    label: subscriber.label
                });
                
                updateStatus('subStatus', `Connected as ${subscriber.label}`, true);
            } catch (error) {
                updateStatus('subStatus', `Error: ${error.message}`, false);
            }
        }
        
        function publishMessage() {
            if (!publisher || !publisher.connected) {
                alert('Publisher not connected');
                return;
            }
            
            const message = document.getElementById('pubMessage').value;
            const target = document.getElementById('pubTarget').value;
            const targetValue = document.getElementById('pubTargetValue').value;
            
            if (!message) return;
            
            const data = {
                message,
                timestamp: new Date().toISOString(),
                from: publisher.label
            };
            
            let options = {};
            if (target === 'label' && targetValue) {
                options.toLabel = targetValue;
            } else if (target === 'streamid' && targetValue) {
                options.toStreamID = targetValue;
            }
            
            publisher.publish(data, options);
            
            addMessage('pubMessages', 
                `Sent to ${target === 'all' ? 'all' : `${target}: ${targetValue}`}: ${message}`, 
                'sent'
            );
            
            document.getElementById('pubMessage').value = '';
        }
        
        async function subscribeToLabel() {
            if (!subscriber || !subscriber.connected) {
                alert('Subscriber not connected');
                return;
            }
            
            const label = document.getElementById('subToLabel').value;
            if (!label) return;
            
            subscriber.subscribe(label);
            
            // Update UI
            const subDiv = document.getElementById('subscriptions');
            const badge = document.createElement('span');
            badge.className = 'label-badge';
            badge.textContent = label;
            subDiv.appendChild(badge);
            
            addMessage('subMessages', `Subscribed to label: ${label}`, 'system');
            
            // Auto-connect to peers with this label
            subscriber.peerLabels.forEach((peerLabel, uuid) => {
                if (peerLabel === label) {
                    const streamID = subscriber.peerStreamIDs.get(uuid);
                    if (streamID) {
                        subscriber.view(streamID);
                    }
                }
            });
        }
        
        async function streamFile() {
            if (!publisher || !publisher.connected) {
                alert('Publisher not connected');
                return;
            }
            
            const fileInput = document.getElementById('fileInput');
            const file = fileInput.files[0];
            if (!file) return;
            
            const reader = new FileReader();
            reader.onload = (e) => {
                const streamId = `file-${Date.now()}`;
                publisher.streamData(streamId, e.target.result, {
                    fileName: file.name,
                    fileType: file.type,
                    fileSize: file.size
                });
                
                addMessage('pubMessages', `Streaming file: ${file.name} (${file.size} bytes)`, 'sent');
            };
            
            reader.readAsArrayBuffer(file);
        }
        
        function handleReceivedStream(stream) {
            const streamsDiv = document.getElementById('streams');
            const streamDiv = document.createElement('div');
            streamDiv.className = 'message';
            
            const blob = new Blob([stream.data], { type: stream.metadata.fileType || 'application/octet-stream' });
            const url = URL.createObjectURL(blob);
            
            streamDiv.innerHTML = `
                <strong>Received Stream:</strong> ${stream.metadata.fileName || 'Unknown'} 
                (${stream.metadata.fileSize || stream.data.byteLength} bytes)
                <a href="${url}" download="${stream.metadata.fileName || 'download'}">Download</a>
            `;
            
            streamsDiv.appendChild(streamDiv);
        }
        
        function updateStatus(elementId, text, connected) {
            const element = document.getElementById(elementId);
            element.textContent = text;
            element.className = 'status' + (connected ? ' connected' : '');
        }
        
        function addMessage(containerId, text, type = 'info') {
            const container = document.getElementById(containerId);
            const message = document.createElement('div');
            message.className = 'message';
            
            const time = new Date().toLocaleTimeString();
            const typeEmoji = {
                system: '🔧',
                sent: '📤',
                received: '📥',
                info: 'ℹ️'
            };
            
            message.innerHTML = `${typeEmoji[type] || ''} [${time}] ${text}`;
            container.appendChild(message);
            container.scrollTop = container.scrollHeight;
        }
        
        function updateMeshVisualization() {
            const canvas = document.getElementById('meshCanvas');
            const ctx = canvas.getContext('2d');
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            
            // Simple mesh visualization
            const nodes = Array.from(meshNodes.values());
            const angleStep = (2 * Math.PI) / nodes.length;
            const radius = 150;
            const centerX = canvas.width / 2;
            const centerY = canvas.height / 2;
            
            // Draw nodes
            nodes.forEach((node, index) => {
                const x = centerX + radius * Math.cos(index * angleStep);
                const y = centerY + radius * Math.sin(index * angleStep);
                
                ctx.beginPath();
                ctx.arc(x, y, 20, 0, 2 * Math.PI);
                ctx.fillStyle = node.type === 'publisher' ? '#4CAF50' : '#2196F3';
                ctx.fill();
                
                ctx.fillStyle = 'white';
                ctx.font = '12px Arial';
                ctx.textAlign = 'center';
                ctx.fillText(node.label || 'Node', x, y + 4);
                
                // Draw connections
                if (node.sdk && node.sdk.peers) {
                    node.sdk.peers.forEach((pc, peerUuid) => {
                        const peerNode = meshNodes.get(peerUuid);
                        if (peerNode) {
                            const peerIndex = nodes.indexOf(peerNode);
                            const peerX = centerX + radius * Math.cos(peerIndex * angleStep);
                            const peerY = centerY + radius * Math.sin(peerIndex * angleStep);
                            
                            ctx.beginPath();
                            ctx.moveTo(x, y);
                            ctx.lineTo(peerX, peerY);
                            ctx.strokeStyle = '#666';
                            ctx.stroke();
                        }
                    });
                }
            });
            
            // Update status text
            const statusDiv = document.getElementById('meshStatus');
            statusDiv.innerHTML = `<strong>Nodes:</strong> ${nodes.length} | 
                <strong>Publishers:</strong> ${nodes.filter(n => n.type === 'publisher').length} | 
                <strong>Subscribers:</strong> ${nodes.filter(n => n.type === 'subscriber').length}`;
        }
        
        // Auto-connect on load for demo
        window.addEventListener('load', () => {
            // Optional: auto-connect for easier testing
            // connectPublisher();
            // connectSubscriber();
        });
    </script>
</body>
</html>